
import java.awt.Font;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.Color;

import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import javax.swing.JOptionPane;

import fr.lri.swingstates.canvas.CEllipse;
import fr.lri.swingstates.canvas.CPolyLine;
import fr.lri.swingstates.canvas.CShape;
import fr.lri.swingstates.canvas.CStateMachine;
import fr.lri.swingstates.canvas.CText;
import fr.lri.swingstates.canvas.CRectangle;
import fr.lri.swingstates.canvas.Canvas;
import fr.lri.swingstates.gestures.AbstractClassifier;
import fr.lri.swingstates.gestures.Gesture;
import fr.lri.swingstates.sm.State;
import fr.lri.swingstates.sm.Transition;
import fr.lri.swingstates.sm.transitions.Drag;
import fr.lri.swingstates.sm.transitions.Press;
import fr.lri.swingstates.sm.transitions.Release;
import fr.lri.swingstates.sm.transitions.TimeOut;
import java.awt.BasicStroke;
import java.awt.Paint;
import java.awt.Stroke;
import javax.swing.JColorChooser;

public class InkMachine extends CStateMachine {
    private static final int SHAPE_HOVERED = 3;
    private static final int SHAPE_NOT_HOVERED = 1;


    private CRectangle rect;
    private CEllipse circle;
    private CText text;
    private Canvas canvas;
    private CShape menu;
    private CShape shapeToColor = null;
    private CShape shapeToCopy = null;
    private CShape shapeCopied = null;
    private CShape shapeCliqued = null;
    private Gesture gesture;
    private CPolyLine ink;
    private Point2D pInit;
    private AbstractClassifier classifier;
    private CText recognizedClass;

    public InkMachine(AbstractClassifier classifier, Canvas canvas) {
        super();
        this.classifier = classifier;
        recognizedClass = canvas.newText(GestureRecognition.WIDTH_TOOL * (classifier.getClasses().size() + 2), 45, "", new Font("Verdana", Font.PLAIN, 14));
        ink = (CPolyLine) canvas.newPolyLine().setPickable(false).setFilled(false);
        gesture = new Gesture();
        menu = pieMenu(30, canvas);

        // Test
        this.canvas = canvas;
    }
    public State start = new State() {

        Transition menu = new PressOnTag("Shape", BUTTON3, ">> menuOn") {

            public void action() {
                shapeToColor = getShape();
                showMenu(getPoint());
            }
        };

        // Pour la fonction copier
        Transition pressOnShape = new PressOnShape(BUTTON1, ">> draw") {

            public void action() {
                pInit = getPoint();
                ink.reset(pInit.getX(), pInit.getY()).setDrawable(true).aboveAll();
                gesture.reset();
                gesture.addPoint(pInit.getX(), pInit.getY());

                // Ajout du shape en mode copy
                shapeCliqued = getShape();
            }
        };
        Transition begin = new Press(BUTTON1, ">> draw") {

            public void action() {
                pInit = getPoint();
                ink.reset(pInit.getX(), pInit.getY()).setDrawable(true).aboveAll();
                gesture.reset();
                gesture.addPoint(pInit.getX(), pInit.getY());
            }
        };
    };
    public State menuOn = new State() {

        Color color = null;
        Transition enterMenuItem = new EnterOnTag(ColorTag.class) {

            public void action() {
                color = ((ColorTag) getTag()).color;
            }
        };

        // Enter on menu center
        Transition test = new LeaveOnTag("menu") {

            public void action() {
                color = null;
            }
        };

        Transition select = new Release(BUTTON3, ">> start") {

            public void action() {
                if (color != null && shapeToColor != null) {
                    // Change the color of the shape
        //            Color couleur = JColorChooser.showDialog(canvas, "Choisissez votre couleur",color);

                    if (color != null)
                        shapeToColor.setFillPaint(color);

                    // Remove the old color and shape
                    color = null;
                    shapeToColor = null;

                }
            }
        };

        // Create a custom color
        Transition custom = new PressOnTag(ColorTag.class, BUTTON1, ">> start") {
            public void action() {
                // Change the color of the shape
                color = JColorChooser.showDialog(canvas, "Choisissez votre couleur",color);
                System.out.println(color);
                if (color != null)
                    shapeToColor.setFillPaint(color);
            }
        };

        public void leave() {
            hideMenu();
        }
    };
    public State draw = new State() {

        Transition drag = new Drag(BUTTON1) {

            public void action() {
                ink.lineTo(getPoint().getX(), getPoint().getY());
                gesture.addPoint(getPoint().getX(), getPoint().getY());
            }
        };

        // On garde ne m�moire le shape a effacer
        Transition dragOnShape = new DragOnShape(BUTTON1) {

            public void action() {
                ink.lineTo(getPoint().getX(), getPoint().getY());
                gesture.addPoint(getPoint().getX(), getPoint().getY());

                // On v�rifie que le tag est bien un rectangle
                if (getShape().hasTag("Shape")) {
                    getShape().setStroke(new BasicStroke(SHAPE_HOVERED));
                    getShape().addTag("D");
                }
            }
        };
        Transition end = new Release(BUTTON1, ">> feedback") {

            public void action() {
                armTimer(500, true);
                gesture.addPoint(getPoint().getX(), getPoint().getY());
                String gc = classifier.classify(gesture);
                CRectangle cadre = ink.getBoundingBox();

                // On réinitialise les bordures
                for (CShape shape:canvas.getDisplayList()) {
                    shape.setStroke(new BasicStroke(SHAPE_NOT_HOVERED));
                }

                if (gc != null) {
                    recognizedClass.setText(gc);

                    if (gc.equals("Nouveau")) {
                        drawRect(getPoint().getX(), getPoint().getY());
                    } else if (gc.equals("Effacer")) {
                        deleteShape();
                    } else if (gc.equals("Coller")) {
                        pastShape();
                        if (shapeToCopy != null)    shapeToCopy.setStroke(new BasicStroke(SHAPE_HOVERED));
                    } else if (gc.equals("Copier")) {
                        shapeToCopy = shapeCliqued;
                        if (shapeToCopy != null)    shapeToCopy.setStroke(new BasicStroke(SHAPE_HOVERED));
                    } else if (gc.equals("Cercle")) {
                        drawCircle(cadre);
                    } else if (gc.equals("Rectangle")) {
                        drawRectangle(cadre);
                    } else if (gc.equals("Ecrire")) {
                        writeText();
                    }

                    // Delete tag of the others tag
                    canvas.removeTag("D");
                } else {
                    recognizedClass.setText("?");
                }
            }

            private void drawRectangle(CRectangle cadre) {
                rect = (CRectangle) canvas.newRectangle(cadre.getMinX(), cadre.getMinY(), cadre.getMaxX() - cadre.getMinX(), cadre.getMaxY() - cadre.getMinY()).setFillPaint(new Color((int) (Math.random() * 254), (int) (Math.random() * 254), (int) (Math.random() * 254))).aboveAll().addTag("Shape");
            }

            private void writeText() {
                // On recupere le texte
                String res = JOptionPane.showInputDialog("Please enter your text:");

                // On affiche le texte ecrit
                if (res != null) {
                    text = (CText) canvas.newText(pInit.getX(), pInit.getY(), res).setFillPaint(new Color((int) (Math.random() * 254), (int) (Math.random() * 254), (int) (Math.random() * 254))).aboveAll().addTag("Shape");
                }
            }

            private void drawCircle(CRectangle cadre) {
                circle = (CEllipse) canvas.newEllipse(cadre.getMinX(), cadre.getMinY(), cadre.getMaxX() - cadre.getMinX(), cadre.getMaxY() - cadre.getMinY()).setFillPaint(new Color((int) (Math.random() * 254), (int) (Math.random() * 254), (int) (Math.random() * 254))).aboveAll().addTag("Shape");
            }

            private void pastShape() {
                if (shapeToCopy != null) {
                    shapeCopied = shapeToCopy.duplicateWithTags();
                    shapeCopied.translateTo(pInit.getX(), pInit.getY()).setDrawable(true);
                }
            }

            private void deleteShape() {
                canvas.removeShapes(canvas.getTag("D"));
            }

            private void drawRect(double xDest, double yDest) {
                double w = xDest - pInit.getX();
                double h = pInit.getY() - yDest;

                double x = pInit.getX() - (w / 2);
                double y = pInit.getY() - (h / 2);
                rect = (CRectangle) canvas.newRectangle(x, y, w, h).setFillPaint(new Color((int) (Math.random() * 254), (int) (Math.random() * 254), (int) (Math.random() * 254))).aboveAll().addTag("Shape");
            }
        };
    };
    public State feedback = new State() {

        Transition timeout = new TimeOut(">> start") {

            public void action() {
                ink.setDrawable(false);
            }
        };
    };

    /**
     * Opens the pie menu at a given location.
     * @param pt the location of the pie menu center
     */
    public void showMenu(Point2D pt) {
        menu.translateTo(pt.getX(), pt.getY());
        canvas.getTag("menu").setDrawable(true).setPickable(true).aboveAll();
    }

    /**
     * Closes the pie menu.
     */
    public void hideMenu() {
        canvas.getTag("menu").setDrawable(false).setPickable(false);
    }

    /**
     * Draws a pie shape.
     * @param canvas The canvas in which this pie must be displayed.
     * @param color The darken of the pie.
     * @param x The x-coordinate of the pie's point.
     * @param y The y-coordinate of the pie's point.
     * @param start the starting angle of the pie (in radians)
     * @param extent The extent of the pie (in radians)
     * @param radius The radius of the pie.
     * @return The pie shape.
     */
    public static CPolyLine pie(Canvas canvas, Color color, double x, double y, double start, double extent, double radius, String tag) {
        CPolyLine itemBackground = canvas.newPolyLine(x, y);
        itemBackground.lineTo(x + radius, y).arcTo(0, extent, radius, radius).lineTo(x, y);
        itemBackground.setReferencePoint(0, 1).rotateBy(-start).setFillPaint(color).setTransparencyFill(0.4f).setAntialiased(true).setDrawable(false);
        // We attach an instance of ColorTag to each menu item.
        // CNamedTag is a predefined SwingStates tag that 
        // associates a string with any Canvas widget shape
        itemBackground.addTag(new ColorTag(color)).addTag(tag);
        return itemBackground;
    }

    public static CShape pieMenu(int radius, Canvas c) {
        CEllipse parent = (CEllipse) c.newEllipse(-2, -2, 4, 4).addTag("menu");
        pie(c, new Color(205, 150, 205), 0, 0, 0, Math.PI / 2, radius, "menu").setParent(parent);
        pie(c, Color.YELLOW, 0, 0, Math.PI / 2, Math.PI / 2, radius, "menu").setParent(parent);
        pie(c, new Color(60, 179, 113), 0, 0, Math.PI, Math.PI / 2, radius, "menu").setParent(parent);
        pie(c, new Color(100, 149, 237), 0, 0, 3 * Math.PI / 2, Math.PI / 2, radius, "menu").setParent(parent);
        parent.aboveAll();
        return parent;
    }
}
